Skip to content

Add <permissions> element validation per SPS 1.10#1133

Open
Copilot wants to merge 2 commits intomasterfrom
copilot/create-validations-permissions-element
Open

Add <permissions> element validation per SPS 1.10#1133
Copilot wants to merge 2 commits intomasterfrom
copilot/create-validations-permissions-element

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 19, 2026

O que esse PR faz?

Implementa 10 regras de validação para o elemento <permissions> conforme SPS 1.10 e Critérios SciELO Brasil:

# Regra Nível
1 Presença de <permissions> em <article-meta> CRITICAL
2 Unicidade de <permissions> ERROR
3 Presença de <license> CRITICAL
4 @license-type="open-access" CRITICAL
5 Presença de @xlink:href CRITICAL
6 Presença de @xml:lang CRITICAL
7 Presença de <license-p> CRITICAL
8 URL CC-BY válida ERROR
9 Consistência @xml:lang@xlink:href (deed suffix) ERROR
10 Estrutura de copyright (year mencionado no statement mas sem <copyright-year>) WARNING

Onde a revisão poderia começar?

packtools/sps/validation/permissions.py — classe PermissionsValidation com todas as regras.

Como este poderia ser testado manualmente?

python -m pytest tests/sps/validation/test_permissions.py -v

39 testes cobrindo todos os cenários: XML válido (en/pt/es, com/sem copyright), atributos ausentes, URLs inválidas, inconsistência idioma/link, duplicidade de <permissions>.

Algum cenário de contexto que queira dar?

  • Já existia ArticleLicenseValidation para validar conteúdo de licença contra dados esperados do journal. Este módulo é complementar — valida a estrutura do elemento <permissions> independente de dados do journal.
  • Níveis de erro são configuráveis via permissions_rules.json.
  • Integrado ao pipeline via xml_validator.py como grupo "permissions".

Arquivos criados:

  • packtools/sps/validation/permissions.py
  • packtools/sps/validation_rules/permissions_rules.json
  • tests/sps/validation/test_permissions.py

Arquivos modificados:

  • packtools/sps/validation/xml_validations.pyvalidate_permissions()
  • packtools/sps/validation/xml_validator.py — yield do grupo "permissions"

Screenshots

N/A

Quais são tickets relevantes?

N/A

Referências

Original prompt

This section details on the original issue you should resolve

<issue_title>Criar validações para o elemento </issue_title>
<issue_description>## Objetivo

Implementar validações para o elemento <permissions> conforme a especificação SPS 1.10 e Critérios SciELO Brasil, aumentando a conformidade de X% para 75% (9 de 12 regras).

Nota: Algumas validações para <permissions> podem já estar parcialmente implementadas no repositório. Este Issue visa reavaliar, complementar e garantir cobertura completa das regras SPS 1.10 e Critérios SciELO Brasil.


Contexto

O elemento <permissions> define condições sob as quais o conteúdo do documento pode ser usado, acessado e distribuído. Para SciELO Brasil é obrigatória a declaração de licença Creative Commons CC-BY. Validações corretas garantem conformidade com políticas de Ciência Aberta, presença de atributos obrigatórios, e consistência entre idioma e links de licença.

Conformidade atual: X de 12 regras implementadas (X%)
Meta após implementação: 9 de 12 regras (75%)


Documentação SPS

Referência oficial: https://docs.google.com/document/d/1GTv4Inc2LS_AXY-ToHT3HmO66UT0VAHWJNOIqzBNSgA/edit?tab=t.0#heading=h.permissions

Regras principais conforme SPS 1.10 e Critérios SciELO Brasil:

  1. Ocorrência:

    • <permissions> deve aparecer uma vez em <article-meta>
  2. Licença obrigatória (SciELO Brasil):

    • Declaração de licença Creative Commons CC-BY é obrigatória
    • Elemento <license> é obrigatório
    • Elemento <license-p> é obrigatório dentro de <license>
  3. Atributos obrigatórios em <license>:

    • @license-type="open-access" (obrigatório)
    • @xlink:href (obrigatório - link CC-BY correspondente ao idioma)
    • @xml:lang (obrigatório - idioma do texto da licença)
  4. Links válidos para @xlink:href por idioma:

    • Português: https://creativecommons.org/licenses/by/4.0/deed.pt
    • Inglês: https://creativecommons.org/licenses/by/4.0/deed.en
    • Espanhol: https://creativecommons.org/licenses/by/4.0/deed.es
  5. Consistência idioma e link:

    • @xml:lang deve corresponder ao idioma do link em @xlink:href
    • Exemplo: xml:lang="pt" → link deve terminar com deed.pt
  6. Texto padrão para <license-p>:

    • Quando PDF não indica texto específico, usar: "This is an open-access article distributed under the terms of the Creative Commons Attribution License"
  7. Elementos de Copyright (condicionais):

    • <copyright-statement> - Quando PDF apresenta declaração de copyright
    • <copyright-year> - Quando há informação de ano
    • <copyright-holder> - Quando há informação do detentor
  8. Conformidade com Critérios SciELO Brasil:

    • Seção 2.3: Ciência Aberta
    • Seção 5.2.4: Qualificação editorial
    • Seção 5.2.10.1: Interoperabilidade

Regras a Implementar

P0 – Críticas (implementar obrigatoriamente)

# Regra Nível Descrição
1 Validar presença de <permissions> CRITICAL O elemento <permissions> é obrigatório em <article-meta> (Critério SciELO Brasil)
2 Validar unicidade de <permissions> ERROR O elemento <permissions> deve aparecer exatamente uma vez em <article-meta>
3 Validar presença de <license> CRITICAL O elemento <license> é obrigatório em <permissions>
4 Validar presença de @license-type="open-access" CRITICAL O atributo @license-type com valor "open-access" é obrigatório em <license>
5 Validar presença de @xlink:href CRITICAL O atributo @xlink:href é obrigatório em <license>
6 Validar presença de @xml:lang CRITICAL O atributo @xml:lang é obrigatório em <license>
7 Validar presença de <license-p> CRITICAL O elemento <license-p> é obrigatório em <license>
8 Validar URL de licença CC-BY ERROR O @xlink:href deve ser um link válido de Creative Commons CC-BY 4.0

P1 – Importantes (implementar se possível)

# Regra Nível Descrição
9 Validar consistência @xml:lang e @xlink:href ERROR O idioma em @xml:lang deve corresponder ao idioma no link @xlink:href (pt→deed.pt, en→deed.en, es→deed.es)
10 Validar estrutura de copyright quando presente WARNING Se houver <copyright-statement>, validar presença de <copyright-year> quando ano estiver mencionado no statement

P2 – Futuras (fora do escopo deste Issue)

# Regra Motivo de exclusão
11 Validar sincronização de texto <license-p> com PDF Alta complexidade - requer análise de PDF externo
12 Validar formato de ano em <copyright-year> Baixa prioridade - schema JATS já valida

Arquivos a Criar/Modificar

Avaliar existentes (podem ter validações parciais):

  • packtools/sps/models/permissions.py ou similar – Verificar se modelo existe
  • `packtools/...

💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

Implement 10 validation rules for the <permissions> element:
1. Presence of <permissions> in <article-meta> (CRITICAL)
2. Uniqueness of <permissions> (ERROR)
3. Presence of <license> (CRITICAL)
4. @license-type="open-access" (CRITICAL)
5. @xLink:href presence (CRITICAL)
6. @xml:lang presence (CRITICAL)
7. <license-p> presence (CRITICAL)
8. Valid CC-BY URL (ERROR)
9. Language/link consistency (ERROR)
10. Copyright structure validation (WARNING)

Files added:
- packtools/sps/validation/permissions.py
- packtools/sps/validation_rules/permissions_rules.json
- tests/sps/validation/test_permissions.py

Files modified:
- packtools/sps/validation/xml_validations.py (integration)
- packtools/sps/validation/xml_validator.py (orchestrator)

Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement validations for permissions element Add <permissions> element validation per SPS 1.10 Mar 19, 2026
Copilot AI requested a review from robertatakenaka March 19, 2026 11:56
Copy link
Copy Markdown
Collaborator

@Rossi-Luciano Rossi-Luciano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Evidência de revisão — permissions.py

Data: 2026-04-23
Revisor: Luciano
Artefatos analisados:

  • permissions.py — módulo de validação
  • permissions_rules.json — parâmetros de configuração
  • permissions.xml — XML artificial gerado para cobertura de testes
  • permissions-2026-23-04-124346-errors.csv — relatório de erros gerado pelo packtools

Metodologia

Análise cruzada entre três artefatos: o módulo .py, o XML artificial construído com o princípio do defeito isolado (um defeito por caso de teste), e o relatório CSV produzido pelo processamento do XML pelo packtools. Para cada caso documentado no XML verificou-se: se o módulo deveria disparar o erro (leitura das regras do código), se o CSV contém a entrada correspondente, se o nível de severidade está correto, e se casos válidos estão corretamente ausentes do CSV.


Regras validadas (10 no total)

# Método Nível configurado
1 validate_permissions_presence CRITICAL
2 validate_permissions_uniqueness ERROR
3 validate_license_presence CRITICAL
4 validate_license_type CRITICAL
5 validate_xlink_href_presence CRITICAL
6 validate_xml_lang_presence CRITICAL
7 validate_license_p_presence CRITICAL
8 validate_license_url ERROR
9 validate_lang_link_consistency ERROR
10 validate_copyright_structure WARNING

Casos de teste no XML artificial

Caso Regra Defeito introduzido Nível esperado
P1 1,3,4,5,6,7,8,9 Nenhum — caso ouro completo OK (ausente do CSV)
C1* 1 <permissions> ausente CRITICAL
C2* 2 <permissions> duplicado ERROR
C3 4 @license-type ausente (obtained=None) CRITICAL
C4 4 @license-type="closed" (valor incorreto) CRITICAL
C5 5 @xlink:href ausente; regras 8 e 9 pulam (href=None) CRITICAL
C6 6 @xml:lang ausente; regra 9 pula (lang=None) CRITICAL
C7 7 <license-p> ausente CRITICAL
C8 8 URL inválida; xml:lang="fr" impede disparo da regra 9 ERROR
C9 9 lang="pt" com URL apontando para deed.en ERROR
C10 10 <copyright-statement> com ano; <copyright-year> ausente WARNING

* C1 e C2 requerem XMLs separados: C1 exige remoção completa do bloco <permissions>;
C2 exige dois blocos <permissions> em <article-meta>, o que introduziria ruído inevitável da regra 3 no mesmo documento.


Resultado da verificação cruzada com o CSV

Todas as 8 entradas esperadas do módulo permissions.py foram encontradas no CSV, sem falsos negativos e sem falsos positivos originados do módulo.

Linha CSV Caso Nível Verificação
14 C3 CRITICAL PASS — @license-type ausente, obtained=None
15 C4 CRITICAL PASS — @license-type="closed"
16 C5 CRITICAL PASS — @xlink:href ausente
17 C6 CRITICAL PASS — @xml:lang ausente
18 C7 CRITICAL PASS — <license-p> ausente
19 C8 ERROR PASS — URL example.com inválida; regra 9 não disparou (lang=fr)
20 C9 ERROR PASS — inconsistência lang=pt / deed.en
21 C10 WARNING PASS — ano "2024" no statement sem <copyright-year>

O caso ouro P1 não aparece no CSV (is_valid=True em todas as regras).
As regras 1, 2 e 3 não geraram entradas no CSV (presença e unicidade satisfeitas;
<license> presente no bloco).


Ruído identificado (outros módulos)

Entradas no CSV não relacionadas ao permissions.py, classificadas como ruído esperado de XMLs artificiais mínimos:

  • Estrutural: abstract, history dates, fig, table-wrap, disp-formula,
    inline-formula, app, subj-group[@subj-group-type="heading"] ausentes.
  • Ambiente local: rendição PDF não encontrada, DOI não registrado no Crossref,
    issue não verificada no Core.
  • Metadados do elemento raiz: specific-use ausente em <article>.
    Efeito colateral não antecipado: o validador de idiomas do artigo (linhas 31–32 do CSV) detectou xml:lang="en" e xml:lang="fr" nas licenças de teste e exigiu títulos de artigo nessas línguas. O uso de xml:lang="fr" no caso C8 introduziu um idioma não presente no restante do documento. Mitigação para próximas criações: usar xml:lang="es" para casos de isolamento, pois é um idioma já mapeado em lang_to_deed e frequentemente presente nos demais casos do XML.

Conclusão

O módulo permissions.py está correto. Todos os casos de teste dispararam os erros esperados com os níveis de severidade corretos, e o caso ouro não gerou nenhuma entrada no CSV. Não foram identificados falsos negativos, falsos positivos ou erros de nível originados do módulo.

@robertatakenaka robertatakenaka marked this pull request as ready for review April 23, 2026 16:50
Copilot AI review requested due to automatic review settings April 23, 2026 16:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds SPS 1.10 / SciELO Brasil structural validation for the <permissions> element and wires it into the existing XML validation pipeline, with a dedicated rule configuration file and a new test suite.

Changes:

  • Introduces PermissionsValidation implementing 10 validation rules for <permissions>/<license> structure and related copyright checks.
  • Adds configurable rule severities and parameters via permissions_rules.json.
  • Integrates the new "permissions" group into the global validator and adds comprehensive unit tests.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packtools/sps/validation/permissions.py New validator implementing <permissions> structural checks and emitting build_response results.
packtools/sps/validation_rules/permissions_rules.json New default rule configuration (levels + URL/lang parameters) for the permissions validator.
packtools/sps/validation/xml_validations.py Adds validate_permissions() entry point to instantiate and run PermissionsValidation.
packtools/sps/validation/xml_validator.py Adds "permissions" as a yielded validation group in the pipeline.
tests/sps/validation/test_permissions.py Adds unit tests covering valid/invalid scenarios and response structure expectations.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +15 to +16
"https://creativecommons.org/licenses/by/",
"http://creativecommons.org/licenses/by/"
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

valid_license_url_patterns is configured as a very broad prefix (.../licenses/by/), which will consider non-4.0 CC-BY URLs valid. If SciELO SPS 1.10 requires CC-BY 4.0, update these patterns to match /licenses/by/4.0/ (and keep Rule 9 for checking the deed.<lang> suffix).

Suggested change
"https://creativecommons.org/licenses/by/",
"http://creativecommons.org/licenses/by/"
"https://creativecommons.org/licenses/by/4.0/",
"http://creativecommons.org/licenses/by/4.0/"

Copilot uses AI. Check for mistakes.
Comment on lines +341 to +346
statement_text = statement.text or ""
copyright_year = perm_node.find("copyright-year")

# Check if statement mentions a year (4 consecutive digits)
year_match = re.search(r"\b(\d{4})\b", statement_text)
if year_match and copyright_year is None:
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In validate_copyright_structure(), statement.text only captures text before any child elements, so a year inside mixed content (e.g., with Copyright © 2025 …) may be missed and the warning won’t trigger. Use the full textual content (e.g., via "".join(statement.itertext())) when searching for the year.

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +126
yield build_response(
title="Permissions uniqueness",
parent=self._parent,
item="article-meta",
sub_item="permissions",
validation_type="value",
is_valid=False,
expected="exactly 1 <permissions> element",
obtained=f"{count} <permissions> elements",
advice="Remove duplicate <permissions> elements. Only one <permissions> should exist in <article-meta>",
data={"permissions_count": count},
error_level=error_level,
)
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In validate_permissions_uniqueness(), the validation_type is set to "value", but this check is conceptually a uniqueness constraint. Other validators in this codebase use validation_type="unique" for the same kind of rule, and keeping it consistent helps downstream consumers categorize results correctly.

Copilot uses AI. Check for mistakes.
Comment on lines +258 to +289
def validate_license_url(self):
"""Rule 8: Validate that @xlink:href is a valid CC-BY URL."""
error_level = self.params.get("license_url_error_level", "ERROR")
valid_patterns = self.params.get(
"valid_license_url_patterns", DEFAULT_VALID_LICENSE_URL_PATTERNS
)
license_nodes = self._get_license_nodes()

for license_node in license_nodes:
href = license_node.get(XLINK_HREF)
if not href:
# Missing href is handled by validate_xlink_href_presence
continue

is_valid = any(href.startswith(pattern) for pattern in valid_patterns)

yield build_response(
title="License URL",
parent=self._parent,
item="license",
sub_item="@xlink:href",
validation_type="value",
is_valid=is_valid,
expected=f"a Creative Commons CC-BY URL starting with one of {valid_patterns}",
obtained=href,
advice=f"Use a valid Creative Commons CC-BY 4.0 URL, e.g. https://creativecommons.org/licenses/by/4.0/",
data={
"xlink_href": href,
"lang": license_node.get(XML_LANG),
},
error_level=error_level,
)
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate_license_url() currently treats any URL starting with .../licenses/by/ as valid, which would allow CC-BY versions other than 4.0 (e.g., /by/3.0/) even though the rule/advice text targets CC-BY 4.0. Tighten this validation (and the configured patterns) to ensure the URL is for CC-BY 4.0 specifically (while still letting Rule 9 handle the deed language suffix).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Criar validações para o elemento <permissions>

4 participants